/*
 * The basic `switch' instruction dispatcher.
 * Copyright (c) 1998 New Generation Software (NGS) Oy
 *
 * Author: Markku Rossi <mtr@ngs.fi>
 */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 */

/*
 * $Source: /usr/local/cvsroot/ngs/js/src/vmswt0.c,v $
 * $Id: vmswt0.c,v 1.8 1998/10/26 14:13:41 mtr Exp $
 */

#include "jsint.h"

/*
 * Types and definitions.
 */

struct function_st {
	JSHeapDestroyableCB destroy;

	char *name;
	unsigned char *code;
	unsigned int length;
};

typedef struct function_st Function;


/*
 * Prototypes for static functions.
 */

static void function_destroy(void *ptr);

static void link_code(JSVirtualMachine * vm, unsigned char *code,
					  unsigned int code_len, unsigned int consts_offset);

static void execute_code(JSVirtualMachine * vm, JSNode * object,
						 Function * f, unsigned int argc, JSNode * argv);


/*
 * Global functions.
 */

int
js_vm_switch0_exec(JSVirtualMachine * vm, JSByteCode * bc,
				   JSSymtabEntry * symtab,
				   unsigned int num_symtab_entries,
				   unsigned int consts_offset,
				   unsigned int anonymous_function_offset,
#ifdef JS_RUNTIME_DEBUG
				   unsigned char *debug_info, unsigned int debug_info_len,
#endif
				   JSNode * object, JSNode * func, unsigned int argc, JSNode * argv)
{
	int i;
	unsigned int ui;
	Function *global_f = NULL;
	Function *f;
	unsigned char *code = NULL;
#ifdef JS_RUNTIME_WARNING
	char buf[512];
#endif

	if (bc) {
		/*
		 * Executing byte-code.
		 */

		/* Find the code section. */
		for (i = 0; i < bc->num_sects; i++)
			if (bc->sects[i].type == JS_BCST_CODE)
				code = bc->sects[i].data;
#ifdef JS_RUNTIME_DEBUG
		assert(code != NULL);
#endif

		/* Enter all functions to the known functions of the VM. */
		for (i = 0; i < num_symtab_entries; i++) {
			/* Need one function. */
			f = js_vm_alloc_destroyable(vm, sizeof(*f));
			f->destroy = function_destroy;
			f->name = js_strdup(vm, symtab[i].name);

			f->length = symtab[i + 1].offset - symtab[i].offset + 1;
			f->code = js_malloc(vm, f->length);
			memcpy(f->code, code + symtab[i].offset, f->length - 1);
			f->code[f->length - 1] = 1;	/* op `done' */

			/* Link the code to our environment. */
			link_code(vm, f->code, f->length, consts_offset);

			if (strcmp(symtab[i].name, JS_GLOBAL_NAME) == 0)
				global_f = f;
			else {
				int is_anonymous = 0;

				/* Check for the anonymous function. */
				if (symtab[i].name[0] == '.' && symtab[i].name[1] == 'F'
					&& symtab[i].name[2] == ':')
					is_anonymous = 1;
#ifdef JS_RUNTIME_WARNING
				if (vm->verbose > 3) {
					sprintf(buf, "VM: link: %s(): start=%d, length=%d",
							symtab[i].name, symtab[i].offset,
							symtab[i + 1].offset - symtab[i].offset);
					if (is_anonymous)
						sprintf(buf + strlen(buf),
								", relocating with offset %u", anonymous_function_offset);
					strcat(buf, JS_HOST_LINE_BREAK);
#ifdef JS_IOSTREAM
					js_iostream_write(vm->s_stderr, buf, strlen(buf));
#else
					fwrite(buf, strlen(buf), 1, stderr);
#endif
				}
#endif

				if (is_anonymous) {
#ifndef JS_RUNTIME_WARNING
					char buf[16];	// CHANGE buffer size, see above code "buf[256]"
#endif
					sprintf(buf, ".F:%u", (unsigned int) atoi(symtab[i].name + 3)
							+ anonymous_function_offset);
					ui = js_vm_intern(vm, buf);
				} else
					ui = js_vm_intern(vm, symtab[i].name);

				vm->globals[ui].type = JS_FUNC;
				vm->globals[ui].u.vfunction = js_vm_make_function(vm, f);
			}
		}
	} else {
		/*
		 * Applying arguments to function.
		 */

		if (func->type != JS_FUNC) {
#ifdef JS_RUNTIME_WARNING
			sprintf(vm->error, "illegal function in apply");
#endif
			return 0;
		}
#ifdef JS_RUNTIME_WARNING
		if (vm->verbose > 1) {
			sprintf(buf, "VM: calling function%s", JS_HOST_LINE_BREAK);
#ifdef JS_IOSTREAM
			js_iostream_write(vm->s_stderr, buf, strlen(buf));
#else
			fwrite(buf, strlen(buf), 1, stderr);
#endif
		}
#endif
		f = func->u.vfunction->implementation;

		execute_code(vm, object, f, argc, argv);
	}

	if (global_f) {
#ifdef JS_RUNTIME_WARNING
		if (vm->verbose > 1) {
			sprintf(buf, "VM: exec: %s%s", global_f->name, JS_HOST_LINE_BREAK);
#ifdef JS_IOSTREAM
			js_iostream_write(vm->s_stderr, buf, strlen(buf));
#else
			fwrite(buf, strlen(buf), 1, stderr);
#endif
		}
#endif

		/* Execute. */
		execute_code(vm, NULL, global_f, 0, NULL);
	}

	return 1;
}

#ifdef JS_RUNTIME_DEBUG
const char *js_vm_switch0_func_name(JSVirtualMachine * vm, void *program_counter)
{
	int i;
	Function *f;
	unsigned char *pc = program_counter;
	JSNode *sp = vm->sp;

	/* Check the globals. */
	for (i = 0; i < vm->num_globals; i++)
		if (vm->globals[i].type == JS_FUNC) {
			f = (Function *) vm->globals[i].u.vfunction->implementation;
			if (f->code < pc && pc < f->code + f->length)
				return f->name;
		}

	/* No luck.  Let's try the stack. */
	for (sp++; sp < vm->stack + vm->stack_size; sp++)
		if (sp->type == JS_FUNC) {
			f = (Function *) sp->u.vfunction->implementation;
			if (f->code < pc && pc < f->code + f->length)
				return f->name;
		}

	/* Still no matches.  This shouldn't be reached... ok, who cares? */
	return JS_GLOBAL_NAME;
}

const char *js_vm_switch0_debug_position(JSVirtualMachine * vm, unsigned int *linenum_return)
{
	/* XXX */
	return NULL;
}
#endif

/*
 * Static functions.
 */

static void function_destroy(void *ptr)
{
	Function *f = ptr;

	js_free(f->name);
	js_free(f->code);
}


static void
link_code(JSVirtualMachine * vm, unsigned char *code,
		  unsigned int code_len, unsigned int consts_offset)
{
	unsigned char *cp, *end;
	JSInt32 i;

	cp = code;
	end = code + code_len;

	while (cp < end) {
		switch (*cp++) {
			/* include c1swt0.h */
#include "c1swt0.h"
			/* end include c1swt0.h */
		}
	}
}


/*
 * Execute byte code by using the `switch' dispatch technique.
 */

#define READ_INT8(i)	JS_BC_READ_INT8(pc, (i)); (i) = (JSInt8) (i); pc++
#define READ_INT16(i)	JS_BC_READ_INT16(pc, (i)); (i) = (JSInt16) (i); pc += 2
#define READ_INT32(i)	JS_BC_READ_INT32(pc, (i)); (i) = (JSInt32) (i); pc += 4

#define SETPC(ofs)		pc = (ofs)
#define SETPC_RELATIVE(ofs)	pc += (ofs)

#define CALL_USER_FUNC(f)	pc = ((Function *) (f))->code

#define DONE() goto done

#ifdef JS_RUNTIME_WARNING
#define ERROR(...) \
  	do { \
    	JS_SAVE_REGS (); \
    	sprintf (vm->error, __VA_ARGS__); \
    	js_vm_error (vm); \
    	/* NOTREACHED */ \
  	} while (0)
#else
#define ERROR(...) \
  	do { \
    	JS_SAVE_REGS (); \
    	js_vm_error (vm); \
    	/* NOTREACHED */ \
  	} while (0)
#endif

static void
execute_code(JSVirtualMachine * vm, JSNode * object, Function * f, unsigned int argc, JSNode * argv)
{
	JSNode *sp;
	JSNode *fp;
	JSNode *function;
	JSNode builtin_result;
	unsigned char *pc;
	JSInt32 i, j;
	JSInt8 i8;
#ifdef JS_RUNTIME_WARNING
	char buf[512];
#endif

	/* Create the initial stack frame by hand. */
	sp = vm->sp;

	/* Protect the function from gc. */
	JS_SP0->type = JS_FUNC;
	JS_SP0->u.vfunction = js_vm_make_function(vm, f);
	JS_PUSH();

	/* Push arguments to the stack. */
	i = argc;
	for (i--; i >= 0; i--) {
		JS_COPY(JS_SP0, &argv[i]);
		JS_PUSH();
	}

	/* This pointer. */
	if (object)
		JS_COPY(JS_SP0, object);
	else
		JS_SP0->type = JS_NULL;
	JS_PUSH();

	/* Init fp and pc so our SUBROUTINE_CALL will work. */
	fp = NULL;
	pc = NULL;

	JS_SUBROUTINE_CALL(f);

	/* Ok, now we are ready to run. */
	while (1) {
		switch (*pc++) {
			/* include eswt0.h */
#include "eswt0.h"
			/* end include eswt0.h */

		default:
#ifdef JS_RUNTIME_WARNING
			sprintf(buf, "execute_code: unknown opcode %d%s", *(pc - 1), JS_HOST_LINE_BREAK);
#ifdef JS_IOSTREAM
			js_iostream_write(vm->s_stderr, buf, strlen(buf));
			js_iostream_flush(vm->s_stderr);
#else
			fwrite(buf, strlen(buf), 1, stderr);
#endif
#endif
			abort();
			break;
		}

		if (vm->enable_interrupt) {
			unsigned char k;
			vm->enable_interrupt = 0;
			for (k = 0; k < 8; k++) {
				JSVMInterrupt *itr = &(vm->interrupt_table[k]);
				if (itr->enable) {
					if (itr->fired) {
						JS_SAVE_REGS();
						if( (*itr->handler)(vm, itr->data) == 0) {
							itr->fired = 0;
						}
						JS_MAYBE_GC();
					}
				}
			}
			vm->enable_interrupt = 1;
		}
	}

done:
	/* All done. */
	JS_COPY(&vm->exec_result, JS_SP1);
}
